/*
 * @Description:
 * @Version: 2.0
 * @Autor: wiz
 * @Date: 2024-07-12 22:15:51
 * @LastEditors: swc shanwenchao12138@163.com
 * @LastEditTime: 2025-09-08 17:59:51
 */
import { ifs, ts, Tsrv } from '@/services';
import { N_cFlowchart, N_oContextMenu } from '@/types';
import { DataUri } from '@antv/x6';
import {
  cellContentMenuItem,
  edgeContentMenuItem,
  nodeContentMenuItem,
  oInteractionConf
} from './config';
import { N_cBpmn } from './types';

type I_graphEventParams =
  | N_cFlowchart.I_eventParams
  | N_cFlowchart.I_edgeConnectParams
  | N_cFlowchart.I_edgeEventParams
  | N_cFlowchart.I_nodeEventParams;
class T_cmd {
  // 主组件箱
  private compBox: ifs.I_compBox;

  // 主组件ID
  private compID: string;

  // 主组件实例
  private self: N_cBpmn.cBpmn;

  private currEventParams: N_cFlowchart.I_eventParams = null;

  private _graphEditorState: N_cFlowchart.I_editorState = 'none';

  private _graphFormData: Array<ifs.I_fv> = [
    new ts.T_fv().setField(Tsrv.globalVar.fieldName.ID).setValue(null),
    new ts.T_fv().setField(Tsrv.globalVar.fieldName.title).setValue(''),
    new ts.T_fv().setField(Tsrv.globalVar.fieldName.fCode).setValue(''),
    new ts.T_fv().setField(Tsrv.globalVar.fieldName.funID).setValue(null),
    new ts.T_fv().setField(Tsrv.globalVar.fieldName.graphData).setValue(''),
    new ts.T_fv().setField(Tsrv.globalVar.fieldName.verNum).setValue(null),
    // new ts.T_fv().setField(Tsrv.globalVar.fieldName.dslCode).setValue(''),
    new ts.T_fv().setField(Tsrv.globalVar.fieldName.m2oFunTitle).setValue(''),
    new ts.T_fv().setField(Tsrv.globalVar.fieldName._audited_).setValue(0)
  ];

  private graphformDataMap: Map<string, ifs.I_fv> = new Map();

  private DSLData: Array<any>; // DSL数据

  constructor() {
    this._graphFormData.forEach(fv => {
      this.graphformDataMap.set(fv.field, fv);
    });
  }

  get graphFormData(): Array<ifs.I_fv> {
    return this._graphFormData;
  }

  set graphFormData(fv: ifs.I_fv) {
    this.graphformDataMap.get(fv.field).setValue(fv.value);
  }

  public getGraphformDataMap() {
    return this.graphformDataMap;
  }

  public regGraphEvents: Map<
    N_cFlowchart.EVENTS,
    (e: I_graphEventParams) => void
  > = new Map();

  public regToolbarEvents: Map<
    string,
    N_cFlowchart.I_toolbarEventFun
  > = new Map();

  // 主组件created里初始化
  public init(self: any, compBoxInfo: ifs.I_compBoxInfo) {
    this.self = self;
    this.compBox = compBoxInfo.TcompBox;
    this.compID = compBoxInfo.compID;
    this.regGraphEvents.set(
      N_cFlowchart.EVENTS.BLANK_CLICK,
      this.graphBlankClick
    );
    this.regGraphEvents.set(
      N_cFlowchart.EVENTS.NODE_CLICK,
      this.graphNodeClick
    );
    this.regGraphEvents.set(
      N_cFlowchart.EVENTS.NODE_DBLCLICK,
      this.graphNodeDbClick
    );
    this.regGraphEvents.set(
      N_cFlowchart.EVENTS.NODE_CONTEXTMENU,
      this.nodeContextMenu
    );
    this.regGraphEvents.set(
      N_cFlowchart.EVENTS.EDGE_CONTEXTMENU,
      this.edgeContextMenu
    );
    // 画布空白处  右键菜单
    // this.regGraphEvents.set(
    //   N_cFlowchart.EVENTS.BLANK_CONTEXTMENU,
    //   this.blankContextMenu
    // );
    // edge边的链接、取消事件
    this.regGraphEvents.set(
      N_cFlowchart.EVENTS.EDGE_CONNECTED,
      this.edgeConnected
    );
    // edge边的添加事件
    // this.regGraphEvents.set(N_cFlowchart.EVENTS.EDGE_ADDED, this.edgeAdded);
    // edge边的删除事件
    // this.regGraphEvents.set(N_cFlowchart.EVENTS.EDGE_REMOVED, this.edgeRemoved);
    this.regGraphEvents.set(N_cFlowchart.EVENTS.NODE_REMOVED, this.nodeRemoved);
    this.regGraphEvents.set(N_cFlowchart.EVENTS.NODE_ADDED, this.nodeAdded);
    this.regToolbarEvents.set('before_save', this.toolbarBeforeSave);
    this.regToolbarEvents.set('before_open', this.toolbarBeforeOpen);
    this.regToolbarEvents.set('before_saveAs', this.toolbarBeforeSaveAs);
    this.regToolbarEvents.set('before_delete', this.toolbarBeforeDelete);
    this.regToolbarEvents.set('before_new', this.toolbarBeforeNew);
    this.regToolbarEvents.set('after_new', this.toolbarAfterNew);
    // 提交审核前事件
    this.regToolbarEvents.set('before_c-audit', this.toolbarBeforeAudit);
    this.regToolbarEvents.set('c-audit', this.toolbarAudit);
    // 撤销审核后事件 （现根据业务逻辑——不能进行反审核）
    // this.regToolbarEvents.set('unAudit', this.toolbarUnaudit);
  }

  public set graphEditorState(state: N_cFlowchart.I_editorState) {
    this._graphEditorState = state;
    this.self.setRightFormModify(/^(add|modify)/.test(state));
  }

  public getTcmd(): T_cmd {
    return this;
  }

  public get graphEditorState(): N_cFlowchart.I_editorState {
    return this._graphEditorState;
  }

  // graph event
  graphBlankClick = (e: N_cFlowchart.I_eventParams) => {
    this.setCurrEventParams(e);
    this.self.cFlowchart.hideContextMenu();
  };

  graphNodeClick = (e: N_cFlowchart.I_eventParams) => {
    this.setCurrEventParams(e);
    this.self.cFlowchart.hideContextMenu();
  };

  graphNodeDbClick = (e: N_cFlowchart.I_eventParams) => {
    this.setCurrEventParams(e);
    this.self.cFlowchart.hideContextMenu();
    if (!this.self.cFlowchart.isDenyEdit()) {
      this.handleNodeDbClick(e);
    }
  };

  toolbarBeforeNew = async (e: N_cFlowchart.cFlowchart): Promise<boolean> => {
    this.graphformDataMap.forEach((v, k) => {
      v.value = '';
    });
    if (this.graphformDataMap.has(Tsrv.globalVar.fieldName._audited_)) {
      this.graphformDataMap.get(Tsrv.globalVar.fieldName._audited_).setValue(0);
    }
    await this.initFormData();
    this.self.cFlowchart.graph.clearCells();
    return true;
  };

  toolbarAfterNew = (e: N_cFlowchart.cFlowchart): Promise<boolean> => {
    return new Promise((resolve, reject) => {
      this.self.cFlowchart.GT_cmd.setBtnState('c-audit', 'disabled', false);
      resolve(true);
    });
  };

  // toolbar event
  toolbarBeforeSave = async (e: N_cFlowchart.cFlowchart): Promise<boolean> => {
    const workID = Tsrv.utils.custUUID(16);
    const uiID = Tsrv.utils.custUUID(16);
    if (this.graphEditorState === 'add') {
      // this.graphformDataMap.get(
      //   Tsrv.globalVar.fieldName.title
      // ).value = Tsrv.utils.custUUID(16);
      // this.graphformDataMap.get(
      //   Tsrv.globalVar.fieldName.fCode
      // ).value = Tsrv.utils.custUUID(16);
      // this.graphformDataMap.get(
      //   Tsrv.globalVar.fieldName.ID
      // ).value = Tsrv.getSnowIDFmtStr();

      this.initFormData();
    }
    try {
      const res = await this.saveDet(workID, uiID);
      if (
        !(
          !Tsrv.noValues(res?.workID, res?.data[uiID]) &&
          res.workID === workID &&
          res.action === 'confirm'
        )
      ) {
        return false;
      }
      res.data[uiID].forEach(fv => {
        this.graphformDataMap.get(fv.field).value = fv.value;
      });
      const saveData = Tsrv.utils.clone(
        this.graphFormData.filter(v => !/^(m2o\||slc\|)/.test(v.field))
      );
      saveData.forEach(fv => {
        if (fv.field === Tsrv.globalVar.fieldName.graphData) {
          const zipResp = wZipStr(JSON.stringify(e.graph.toJSON()));
          if (zipResp.isErr()) {
            throw new Error(zipResp.getErrMsg());
          }
          fv.value = zipResp.getReturnData('data');
        }
      });
      // 剔除审核字段
      const findAuditIdx = saveData.findIndex(
        itm => itm.field === Tsrv.globalVar.fieldName._audited_
      );
      if (findAuditIdx !== -1) {
        saveData.splice(findAuditIdx, 1);
      }
      const b = await this.doSave(e, saveData);
      return b;
    } catch (er) {
      return er;
    }
  };

  toolbarBeforeSaveAs = async (
    e: N_cFlowchart.cFlowchart
  ): Promise<boolean> => {
    const workID = Tsrv.utils.custUUID(16);
    const uiID = Tsrv.utils.custUUID(16);
    const rd = await this.resetFormData();
    const res = await this.saveDet(workID, uiID, rd);
    if (
      !(
        !Tsrv.noValues(res?.workID, res?.data[uiID]) &&
        res.workID === workID &&
        res.action === 'confirm'
      )
    ) {
      return false;
    }
    res.data[uiID].forEach(fv => {
      this.graphformDataMap.get(fv.field).value = fv.value;
    });
    const saveData = Tsrv.utils.clone(
      this.graphFormData.filter(v => !/^(m2o\||slc\|)/.test(v.field))
    );
    const newId = await Tsrv.getSnowIDFmtStr();
    for (const fv of saveData) {
      if (fv.field === Tsrv.globalVar.fieldName.ID) {
        fv.value = newId;
      }
      if (fv.field === Tsrv.globalVar.fieldName.graphData) {
        const zipResp = wZipStr(JSON.stringify(e.graph.toJSON()));
        if (zipResp.isErr()) {
          throw new Error(zipResp.getErrMsg());
        }
        fv.value = zipResp.getReturnData('data');
      }
    }
    // 剔除审核字段
    const findAuditIdx = saveData.findIndex(
      itm => itm.field === Tsrv.globalVar.fieldName._audited_
    );
    if (findAuditIdx !== -1) {
      saveData.splice(findAuditIdx, 1);
    }
    // 修改id、添加图数据
    this.graphEditorState = 'add';
    const b = await this.doSave(e, saveData);
    return b;
  };

  toolbarBeforeDelete = (e: N_cFlowchart.cFlowchart): Promise<boolean> => {
    return new Promise((resolve, reject) => {
      this.self.$Modal.confirm({
        title: Tsrv.getI18nByValue('提示'),
        content: Tsrv.getI18nByValue('确定删除'),
        closable: true,
        onOk: () => {
          this.doDel().then(ok => {
            if (ok) {
              resolve(true);
            } else {
              resolve(false);
            }
          });
        },
        onCancel: () => {
          resolve(false);
        }
      });
    });
  };

  toolbarBeforeOpen = (e: N_cFlowchart.cFlowchart): Promise<boolean> => {
    return new Promise((resolve, reject) => {
      this.self.showModal('open');
      resolve(true);
    });
  };

  toolbarBeforeAudit = (e: N_cFlowchart.cFlowchart): Promise<boolean> => {
    return new Promise((resolve, reject) => {
      // 1.1、计算生成 DSL，同步 “BPMN图库” 数据库表
      // 1.2、修改审核状态，同步 “BPMN图库” 数据库表
      // 2、审核成功后：添加内部联络单 ==同步更新日志==
      const graph = this.self.cFlowchart.graph;
      const DSLData: Array<any> = [];
      const nodes: Array<any> = [];
      /**
       * @description: 1、检查该审核流程的合理性
       */
      const [ok, err] = this.checkApprovalData();
      if (!ok) {
        this.self.$Message.error({
          content: err
        });
        reject(false);
        return;
      }
      const beginNode = graph.getRootNodes()[0];
      const endNode = graph.getLeafNodes()[0];
      nodes.push(...graph.getPredecessors(endNode), endNode);
      nodes.forEach(node => {
        const nodeProp = node.getProp();
        const data = node.getData();
        const previous = [];
        const subsequent = [];
        const previousNodes = graph.getNeighbors(node, { incoming: true });
        const subsequentNodes = graph.getNeighbors(node, { outgoing: true });
        if (nodeProp.shape === 'bpmn-begin' || nodeProp.shape === 'bpmn-end') {
          return;
        }
        if (previousNodes.length > 0) {
          for (let i = 0; i < previousNodes.length; i++) {
            const itemProp = previousNodes[i].getProp();
            if (itemProp.shape === 'bpmn-begin') {
              continue;
            }
            const pParam = {
              status: 0,
              type:
                itemProp.shape === 'bpmn-activity'
                  ? 0
                  : itemProp.shape === 'bpmn-gateway'
                  ? 1
                  : itemProp.shape === 'bpmn-activities'
                  ? 2
                  : -1,
              nodeCode: itemProp.id,
              data: previousNodes[i].getData()
            };
            previous.push(pParam);
          }
        }
        if (subsequentNodes.length > 0) {
          for (let j = 0; j < subsequentNodes.length; j++) {
            const itemProp = subsequentNodes[j].getProp();
            if (itemProp.shape === 'bpmn-end') {
              continue;
            }
            const sParam = {
              status: 0,
              type:
                itemProp.shape === 'bpmn-activity'
                  ? 0
                  : itemProp.shape === 'bpmn-gateway'
                  ? 1
                  : itemProp.shape === 'bpmn-activities'
                  ? 2
                  : -1,
              nodeCode: itemProp.id,
              data: subsequentNodes[j].getData()
            };
            subsequent.push(sParam);
          }
        }
        DSLData.push({
          status: 0,
          type:
            nodeProp.shape === 'bpmn-activity'
              ? 0
              : nodeProp.shape === 'bpmn-gateway'
              ? 1
              : nodeProp.shape === 'bpmn-activities'
              ? 2
              : -1,
          nodeCode: nodeProp.id, // 即nodeId
          data, // id, nodeId, checker, condition
          previous, // item （之前的）
          subsequent // item （之后的）
        });
      });
      this.DSLData = DSLData;
      resolve(true);
    });
  };

  // bpmn审核
  toolbarAudit = (
    e: N_cFlowchart.cFlowchart,
    auditStatus: number = 1
  ): Promise<boolean> => {
    return new Promise((resolve, reject) => {
      const dbType = Tsrv.globalVar.dbType.MYSQL;
      const tableName = Tsrv.globalVar.tableName.BPMN_LIB;
      const rule = {} as ifs.I_rule;
      const upd = new ts.T_ctdUpdRec();
      const filter = {
        id: this.graphformDataMap.get(Tsrv.globalVar.fieldName.ID).value
      };
      const updData = [
        // new ts.T_fv().setField('audit_status').setValue(auditStatus),
        new ts.T_fv().setField('_audited_').setValue(auditStatus),
        new ts.T_fv()
          .setField('dsl_code')
          .setValue(JSON.stringify(this.DSLData))
      ]; // 更新数据
      upd.setData({}, filter, {} as ifs.I_rule, ...updData);
      Tsrv.updRows(tableName, dbType, rule, upd)
        .then(([ok, res, err]) => {
          if (!ok || !err) {
            this.self.$msg(err ?? res.msg);
            resolve(false);
          } else {
            this.self.cFlowchart.GT_cmd.setBtnState(
              'c-audit',
              'disabled',
              !!auditStatus
            );
            this.self.$msg(res.msg);
            resolve(true);
          }
        })
        .catch(er => {
          reject(er);
        });
    });
  };

  // bpmn反审核
  toolbarUnaudit = (e: N_cFlowchart.cFlowchart): Promise<boolean> => {
    return this.toolbarAudit(e, 0);
  };

  private saveDet(
    workID: string,
    uiID: string,
    graphFormData?: Array<ifs.I_fv>
  ): Promise<any> {
    const formData = graphFormData ?? this.graphFormData;
    return Tsrv.utils.createModal(
      'oInteraction',
      oInteractionConf(workID, uiID, formData)
    );
  }

  private doSave(
    e: N_cFlowchart.cFlowchart,
    data: ifs.I_fv[]
  ): Promise<boolean> {
    return new Promise((resolve, reject) => {
      const dbType = Tsrv.globalVar.dbType.MYSQL;
      const tableName = Tsrv.globalVar.tableName.BPMN_LIB;
      const rule = {} as ifs.I_rule;

      if (this.graphEditorState === 'add') {
        const ins = new ts.T_ctdInsRec();
        ins.setScope({}).setBaseDataGeneral(rule, {}, ...data);
        Tsrv.addRows(tableName, dbType, rule, ins)
          .then(([ok, res, err]) => {
            if (!ok || !err) {
              this.self.$msg(err ?? res.msg);
              resolve(false);
            } else {
              this.self.$msg(res.msg);
              resolve(true);
            }
          })
          .catch(er => {
            reject(er);
          });
      } else if (this.graphEditorState === 'modify') {
        const upd = new ts.T_ctdUpdRec();
        const filter = {
          id: this.graphformDataMap.get(Tsrv.globalVar.fieldName.ID).value
        };
        data = data.filter(el => el.field !== 'id');
        upd.setData({}, filter, {} as ifs.I_rule, ...data);
        Tsrv.updRows(tableName, dbType, rule, upd)
          .then(([ok, res, err]) => {
            if (!ok || !err) {
              this.self.$msg(err ?? res.msg);
              resolve(false);
            } else {
              this.self.$msg(res.msg);
              resolve(true);
            }
          })
          .catch(er => {
            reject(er);
          });
      }
    });
  }

  private doDel() {
    return new Promise<boolean>((resolve, reject) => {
      const id = this.graphformDataMap.get(Tsrv.globalVar.fieldName.ID).value;
      if (Tsrv.noValue(id)) {
        resolve(false);
        return;
      }
      const p = new ts.T_ctdDelRec()
        .setFilter({
          id: { $eq: id }
        })
        .setRule({} as ifs.I_rule);

      Tsrv.delRows(
        Tsrv.globalVar.tableName.BPMN_LIB,
        Tsrv.globalVar.dbType.MYSQL,
        {} as ifs.I_rule,
        p
      ).then(([ok, res, err]) => {
        this.self.$msg(err ?? res.msg);
        if (!ok || !err) {
          resolve(false);
        }
        resolve(true);
      });
    });
  }

  nodeContextMenu = (e: N_cFlowchart.I_eventParams) => {
    this.setCurrEventParams(e);
    const p: N_oContextMenu.I_showPosition = {
      clientX: e.e.clientX,
      clientY: e.e.clientY
    };
    this.self.nodeContentMenu(p, e);
  };

  edgeContextMenu = (e: N_cFlowchart.I_eventParams) => {
    this.setCurrEventParams(e);
    const p: N_oContextMenu.I_showPosition = {
      clientX: e.e.clientX,
      clientY: e.e.clientY
    };
    this.self.edgeContentMenu(p, e);
  };

  /**
   * @description: 添加edge连线事件回调
   * @return {*}
   * @author: swc
   * @Date: 2024-08-26 19:47:58
   * @LastEditors: zss
   * @LastEditTime: Do not edit
   */
  // edgeAdded({ edge, index, options }) {
  edgeAdded = ({ edge, index, options }) => {
    // const graph = this.self.cFlowchart.graph; // node.model.graph
    // // 线不能连接线
  };

  /**
   * @description: 连线————连接事件——> 1、修改节点关系（）——> 2、
   * @description: 重要：——结合edge:remove事件实现（）——> 删除连线则删除两节点间的父子关系
   * @description: 现将修改：不设置父关系，只设置子关系
   * @param {N_cFlowchart} e
   * @return {*}
   * @author: swc
   * @Date: 2024-08-26 19:23:47
   * @LastEditors: swc
   * @LastEditTime: Do not edit
   */
  edgeConnected = (e: N_cFlowchart.I_edgeConnectParams) => {
    const graph = this.self.cFlowchart.graph;
    const edge = e.edge;
    const source = edge.getSourceNode();
    // （建立在连线规则上：1:线不能连接线、2:连线如果没有target，该连线不成立——即：自动删除）
    const target = edge.getTargetNode();
    // e.type —— source, target
    if (e.type === 'target') {
      const sourceProp = source.getProp();
      const sourceEdges = graph.getOutgoingEdges(source.id);
      const targetProp = target.getProp();
      // 连线规则
      // 规则一：两个节点之间不能相互连接
      const targetOutgoingEdges = graph.getOutgoingEdges(target.id);
      if (!Tsrv.noValue(targetOutgoingEdges)) {
        if (
          targetOutgoingEdges.find(
            item => item.getTargetNode().id === source.id
          )
        ) {
          graph.removeEdge(edge.id);
        }
      }
      // 规则二：除规则节点外，其他节点不能有多个出边
      if (sourceEdges.length > 1 && sourceProp.shape !== 'bpmn-gateway') {
        graph.removeEdge(edge.id);
      }
      // 规则三：规则节点 的下级节点不能是 规则节点
      if (
        sourceProp.shape === 'bpmn-gateway' &&
        targetProp.shape === 'bpmn-gateway'
      ) {
        graph.removeEdge(edge.id);
      }
      // 修改的边（现系统不支持）
      // if (!e.isNew) {
      //   // 基于连线规则，该可能性不存在。（避免后期连线规则改变，先做逻辑）
      //   if (e.previousCell) {}
      // }
    } else {
      // source （现系统不支持 倒箭头）
      // 基于连线规则，该可能性不存在。（避免后期连线规则改变，先做逻辑）
      // 新创建的边（应该不存在此可能性：因为edge暂时不存在“倒箭头”的线条）
      if (source) {
        //
      }
      // 修改的边（现系统不支持）
      // if (!e.isNew) {
      //   // 基于连线规则，该可能性不存在。（避免后期连线规则改变，先做逻辑）
      //   if (e.previousCell) {
      //     //
      //   }
      // }
    }
    /**
     * 以下是：连线则关联两节点间的父子关系（暂时弃用）
     */
    // const edge = e.edge;
    // // e.type —— source, target
    // if (e.type === 'target') {
    //   const source = edge.getSourceNode();
    //   // （建立在连线规则上：1:线不能连接线、2:连线如果没有target，该连线不成立——即：自动删除）
    //   const target = edge.getTargetNode();
    //   if (target) {
    //     source.setChildren([target], { silent: false });
    //     target.setParent(source, { silent: false }); // silent为 true 时不触发 'change:parent' 事件和画布重绘。
    //   }
    //   if (!e.isNew) {
    //     // 修改的边
    //     // 基于连线规则，该可能性不存在。（避免后期连线规则改变，先做逻辑）
    //     if (e.previousCell) source.removeChild(e.previousCell, { deep: false }); // deep:默认递归移除所有子节点/边，设置为 false 时只移除当前节点/边。
    //     // 缺少操作（暂无解）：删除制定父节点关系
    //   }
    // } else {
    //   // source
    //   // 基于连线规则，该可能性不存在。（避免后期连线规则改变，先做逻辑）
    //   const target = edge.getTargetNode();
    //   // 新创建的边（应该不存在此可能性：因为edge暂时不存在“倒箭头”的线条）
    //   const source = edge.getSourceNode();
    //   if (source) {
    //     source.setChildren([target], { silent: false });
    //     target.setParent(source, { silent: false });
    //   }
    //   if (!e.isNew) {
    //     // 修改的边
    //     // 基于连线规则，该可能性不存在。（避免后期连线规则改变，先做逻辑）
    //     if (e.previousCell) e.previousCell.removeChild(target, { deep: false });
    //   }
    // }
  };

  /**
   * @description: 删除边edge事件回调
   * @return {*}
   * @author: swc
   * @Date: 2024-08-26 19:47:58
   * @LastEditors: zss
   * @LastEditTime: Do not edit
   */
  edgeRemoved({ edge, index, options }) {
    const source = edge.getSourceNode();
    const target = edge.getTargetNode();
    if (!Tsrv.noValue(source) && !Tsrv.noValue(target)) {
      if (source.isParentOf(target)) {
        source.removeChild(target, { deep: false });
        target.setParent(null, { silent: false });
      }
    }
  }

  /**
   * @description: 删除节点node事件回调
   * @return {*}
   * @author: swc
   * @Date: 2024-08-26 19:47:58
   * @LastEditors: zss
   * @LastEditTime: Do not edit
   */
  nodeRemoved({ node, index, options }) {
    if (node.hasParent()) {
      node.removeFromParent();
    }
  }

  /**
   * @description: 添加节点node事件回调
   * @return {*}
   * @author: swc
   * @Date: 2024-08-26 19:47:58
   * @LastEditors: zss
   * @LastEditTime: Do not edit
   */
  nodeAdded({ node, index, options }) {
    // const graph = this.self.cFlowchart.graph; // node.model.graph
    const nodeProp = node.getProp();
    const data = node.getData();
    const define = {
      overwrite: false, // 是否覆盖
      deep: false, // 是否深度覆盖
      silent: true // 是否静默修改——是否触发 'change:data' 事件和画布重绘
    };
    node.setData(
      { id: nodeProp.id, nodeID: Tsrv.utils.custUUID(8, 16) },
      define
    );
    // 添加cell（节点/边）校验（——节点是“开始”“结束”唯一——后期替换c-flowchat文件中的addCellValidate——）
    // if (!Tsrv.noValue(data)) {
    //   if (Tsrv.utils.get(data, 'unique', false)) {
    //     if (graph.getNodes().filter(el => el.shape === node.shape).length > 1) {
    //       graph.removeNode(node.id);
    //     }
    //   }
    // }
  }

  blankContextMenu = (e: N_cFlowchart.I_eventParams) => {
    this.setCurrEventParams(e);
    const p: N_oContextMenu.I_showPosition = {
      clientX: e.e.clientX,
      clientY: e.e.clientY
    };
    this.self.blankContentMenu(p, e);
  };

  private setCurrEventParams(e: N_cFlowchart.I_eventParams) {
    this.currEventParams = null;
    if (!Tsrv.noValue(e)) {
      this.currEventParams = e;
    }
  }

  public flowchartContextMenuSelected(
    key: string,
    cmd: N_cFlowchart.I_cFlowCmd
  ) {
    this.self.cFlowchart.hideContextMenu();
    // const k = key.split(':')[0];
    switch (key) {
      case 'add:node:bpmn-activity':
        // this.addNActivityAnddEdge('bpmn-activity');
        this.addNode('bpmn-activity');
        break;
      case 'modify:cell:data':
        this.openNodeEditor();
        break;
      case 'copyAdd:cell':
        this.copyAndAddCell();
        break;
      case 'export:blank:svg':
        this.exportGraph('svg');
        break;
      case 'export:blank:png':
        this.exportGraph('png');
        break;
      case 'export:blank:jpeg':
        this.exportGraph('jpeg');
        break;
      case 'debug:debug':
        this.debug();
        break;
      default:
        break;
    }
  }

  private addNActivityAnddEdge(shape: string) {
    const targetNodeID = this.addNode(shape);
    if (this.currEventParams.cell.isNode()) {
      this.addEdge(this.currEventParams.node.id, targetNodeID);
      this.self.cFlowchart.layout();
    }
  }

  private addNode(shape: string): string {
    const nodeID = Tsrv.utils.custUUID(16);
    const nodeParams = {
      shape,
      id: nodeID,
      // height: 40,
      // width: 80
      height: 45,
      width: 45
    };
    // 如果当前有选中的节点，则新节点在选中节点右侧
    const targetNode = this.self.cFlowchart.graph
      .getSelectedCells()
      .find(cell => {
        return cell.shape === shape && cell.isNode();
      });
    if (!Tsrv.noValue(targetNode) && !Tsrv.noValue(targetNode['position'])) {
      nodeParams['x'] = targetNode['position']().x + 50;
      nodeParams['y'] = targetNode['position']().y + 50;
    }

    this.self.cFlowchart.addNode(nodeParams);
    return nodeID;
  }

  private addEdge(source: string, target: string) {
    this.self.cFlowchart.addEdge({
      source,
      target
    });
  }

  private openNodeEditor() {
    const cell = this.self.cFlowchart.graph.getSelectedCells()[0];
    if (cell.isNode()) {
      // this.self.showNodeModal(cell);
      const cellProp = cell.getProp();
      if (
        cellProp.shape === 'bpmn-activity' ||
        cellProp.shape === 'bpmn-gateway'
      ) {
        this.self.showNodeModal(cell);
      } else {
        // 暂时使用该提示，后续在右键菜单上修改——非指定节点不显示“修改属性”或为disabled
        this.self.$Message.info({
          content: '暂不支持修改该类型节点'
        });
      }
    } else if (cell.isEdge()) {
      //
    }
  }

  /**
   * @description: 节点双击事件
   * @param {N_cFlowchart} e
   * @return {*}
   */
  private handleNodeDbClick(e: N_cFlowchart.I_eventParams) {
    const event = e.e;
    const node = e.node;
    const cell = e.cell;
    const prop = node.getProp();
    const shape = prop.shape;
    switch (shape) {
      case 'bpmn-activity':
        this.openMagicBoxByCell('_sys_user', e);
        break;
      case 'bpmn-gateway':
        this.openMagicBoxByCell('_sys_publicprocess', e, {
          isFunLayout: false
        });
        break;
      case 'bpmn-activities':
        this.openMagicBoxByCell('_sys_user_group', e);
        break;

      default:
        break;
    }
  }

  private openMagicBoxByCell(
    funName: string,
    e: N_cFlowchart.I_eventParams,
    cfg: any = {}
  ) {
    // const event = e.e;
    const node = e.node;
    const cell = e.cell;
    const config = {
      isFunLayout: true
    };
    if (!Tsrv.noValue(cfg)) {
      Object.assign(config, cfg);
    }
    config['toolbarConfig'] = {
      left: false,
      right: true
    };
    const filter = {};
    Tsrv.utils
      .createModal('oMagicBox', {
        source: {
          funid: null,
          isRule: true,
          field: funName,
          limit: 999,
          filter
        },
        config
      })
      .then(selectedData => {
        if (Tsrv.noValue(selectedData)) {
          return;
        }
        if (!Array.isArray(selectedData)) {
          this.self.$msg('不合法数据');
          return;
        }
        if (cell.isNode()) {
          const prop = node.getProp();
          const shape = prop.shape;
          const rowData = selectedData[0];
          const define = {
            overwrite: false, // 是否覆盖
            deep: false, // 是否深度覆盖
            silent: true // 是否静默修改——是否触发 'change:data' 事件和画布重绘
          };
          let d = null;
          if (shape === 'bpmn-activity') {
            d = {
              checker: {
                value: rowData.user_no,
                label: rowData.user_name
              }
            };
            node.setAttrByPath('label/text', d.checker.label);
          }
          if (shape === 'bpmn-gateway') {
            d = {
              condition: {
                value: rowData.f_name,
                label: rowData.f_name
              },
              comment: rowData?.f_comment ?? ''
            };
            // node.setAttrByPath('label/text', d.condition.label);
          }
          if (shape === 'bpmn-activities') {
            d = {
              value: rowData.id,
              label: rowData?.title ?? '空',
              comment: rowData?.f_comment ?? ''
            };
            node.setAttrByPath('label/text', d.label);
          }
          node.setData(d, define);
        }
      });
  }

  /**
   * @description: 修改节点或边的自定义数据（应用于业务）
   * @return {*}
   * @author: swc
   * @Date: 2024-08-20 18:19:56
   * @LastEditors: swc
   * @LastEditTime: Do not edit
   */
  public modifyCellData(cellId: string, data: any) {
    const cell = this.self.cFlowchart.graph.getCellById(cellId);
    if (cell.isNode()) {
      const define = {
        overwrite: false, // 是否覆盖
        deep: false, // 是否深度覆盖
        silent: true // 是否静默修改——是否触发 'change:data' 事件和画布重绘
      };
      cell.setData(data, define);
      if (cell.shape === 'bpmn-activity' && !Tsrv.noValue(data?.checker)) {
        const checker = data.checker;
        cell.setAttrByPath(
          'label/text',
          Tsrv.getI18nBySrv(checker.label) ?? Tsrv.getI18nBySrv(checker.value)
        );
      }
    } else if (cell.isEdge()) {
      // this.self.edgeDataModify(cell);
    }
  }

  /**
   * @description: 复制新增cell
   * @return {*}
   * @author: swc
   * @Date: 2024-08-20 18:19:18
   * @LastEditors: swc
   * @LastEditTime: Do not edit
   */
  private copyAndAddCell() {
    // bugger：复制粘贴后，粘贴的节点和边不能选中（多选情况下）
    const cells = this.self.cFlowchart.graph.getSelectedCells();
    if (cells.length > 0) {
      this.self.cFlowchart.graph.copy(cells);
      this.self.cFlowchart.graph.paste({ offset: 20 });
    }
  }

  public checkApprovalData(): Array<any> {
    const graph = this.self.cFlowchart.graph;
    const rootNodes = graph.getRootNodes();
    const leafNodes = graph.getLeafNodes();
    if (rootNodes.length !== 1) {
      return [false, Tsrv.getI18nByValue('该流程图存在多个根节点，请检查')];
    }
    if (
      rootNodes.length === 1 &&
      rootNodes[0].getProp().shape !== 'bpmn-begin'
    ) {
      return [false, Tsrv.getI18nByValue('该流程图根节点类型不正确，请检查')];
    }
    if (leafNodes.length !== 1) {
      return [false, Tsrv.getI18nByValue('该流程图存在多个叶子节点，请检查')];
    }
    if (leafNodes.length === 1 && leafNodes[0].getProp().shape !== 'bpmn-end') {
      return [false, Tsrv.getI18nByValue('该流程图叶子节点类型不正确，请检查')];
    }
    return [true];
  }

  debug() {
    const graph = this.self.cFlowchart.graph;
    const cells = this.self.cFlowchart.graph.getSelectedCells();
  }

  public exportGraph(type: 'jpeg' | 'png' | 'svg') {
    switch (type) {
      case 'svg':
        this.self.cFlowchart.graph.toSVG(
          (dataUri: string) => {
            DataUri.downloadDataUri(dataUri, 'chart.png');
          },
          {
            preserveDimensions: true,
            // viewBox: '',
            copyStyles: false
            // stylesheet: '',
            // serializeImages: true,
            // beforeSerialize: (that: N_cFlowchart.x6Graph, svg: SVGSVGElement) => {}
          }
        );
        break;
      case 'png':
        this.self.cFlowchart.graph.toPNG(
          (dataUri: string) => {
            DataUri.downloadDataUri(dataUri, 'chart.png');
          },
          {
            copyStyles: false,
            // width: number,
            // height: number,
            // backgroundColor: string,
            padding: {},
            quality: 1
          }
        );
        break;
      case 'jpeg':
        this.self.cFlowchart.graph.toPNG(
          (dataUri: string) => {
            DataUri.downloadDataUri(dataUri, 'chart.png');
          },
          {
            copyStyles: false,
            // width: number,
            // height: number,
            // backgroundColor: string,
            padding: {},
            quality: 1
          }
        );
        break;
      default:
        break;
    }
  }

  /**
   * @description: 动态调整右鍵菜單的菜單項
   * @param {*} eventParams
   * @return {*}
   * @author: swc
   * @Date: 2024-08-28 16:17:36
   * @LastEditors: zss
   * @LastEditTime: Do not edit
   */
  public handleMenuItems(
    eventParams: N_cFlowchart.I_eventParams
  ): Array<N_oContextMenu.I_menuItem> {
    const cell = eventParams.cell;
    const contentMenuItem: Array<N_oContextMenu.I_menuItem> = [
      ...cellContentMenuItem()
    ];
    if (cell.isNode()) {
      contentMenuItem.unshift(...nodeContentMenuItem());
      if (cell.shape === 'bpmn-begin' || cell.shape === 'bpmn-end') {
        contentMenuItem.find(
          v => v.name === 'modify:cell:data'
        ).disabled = true;
      }
    }
    if (cell.isEdge()) {
      contentMenuItem.unshift(...edgeContentMenuItem());
      contentMenuItem.find(v => v.name === 'copyAdd:cell').isShow = false;
      contentMenuItem.find(v => v.name === 'modify:cell:data').isShow = false;
    }
    return contentMenuItem;
  }

  public loadBpmnData(row: any) {
    this.self.cFlowchart.GT_cmd.setBtnState(
      'c-audit',
      'disabled',
      // !!row.audit_status
      !!row._audited_
    ); // 设置审核按钮可用
    this.graphformDataMap.forEach((v, k) => {
      Object.keys(row).forEach(key => {
        if (key === k) {
          v.value = row[key];
        }
      });
    });
    let graphData = this.graphformDataMap.get(
      Tsrv.globalVar.fieldName.graphData
    ).value as string;
    if (!Tsrv.utils.isJSONString(graphData)) {
      graphData = JSON.stringify(graphData);
    }
    this.self.cFlowchart.reLoadDataFromJson(graphData);
    this.self.setRightFormValue();
  }

  private initFormData() {
    this.setFCodeOfForm();
    this.setTitleOfForm();
    this.setIdOfForm();
    this.self.setRightFormValue();
  }

  public setIdOfForm() {
    const v = this.graphformDataMap.get(Tsrv.globalVar.fieldName.ID);
    if (Tsrv.noValue(v.value)) {
      Tsrv.getSnowIDFmtStr().then(id => {
        v.value = id;
      });
    }
  }

  public setFCodeOfForm(val?: string) {
    const v = this.graphformDataMap.get(Tsrv.globalVar.fieldName.fCode);
    if (!Tsrv.noValue(val)) {
      v.value = val;
      return;
    }
    if (Tsrv.noValue(v.value)) {
      v.value = Tsrv.utils.custUUID(8);
    }
  }

  public setTitleOfForm(val?: string) {
    const v = this.graphformDataMap.get(Tsrv.globalVar.fieldName.title);
    if (!Tsrv.noValue(val)) {
      v.value = val;
      return;
    }
    if (Tsrv.noValue(v.value)) {
      v.value = Tsrv.utils.custUUID(8);
    }
  }

  public setVerNumOfForm(val: number) {
    this.graphformDataMap.get(Tsrv.globalVar.fieldName.verNum).value = val;
  }

  public setFunIdOfForm(funTitle: string, funID: number) {
    this.graphformDataMap.get(Tsrv.globalVar.fieldName.funID).value = funID;
    this.graphformDataMap.get(
      Tsrv.globalVar.fieldName.m2oFunTitle
    ).value = funTitle;
  }

  public async resetFormData(): Promise<Array<ifs.I_fv>> {
    const id = await Tsrv.getSnowIDFmtStr();
    return [
      new ts.T_fv().setField(Tsrv.globalVar.fieldName.ID).setValue(id),
      new ts.T_fv()
        .setField(Tsrv.globalVar.fieldName.title)
        .setValue(Tsrv.utils.custUUID(8)),
      new ts.T_fv()
        .setField(Tsrv.globalVar.fieldName.fCode)
        .setValue(Tsrv.utils.custUUID(8)),
      new ts.T_fv().setField(Tsrv.globalVar.fieldName.funID).setValue(''),
      new ts.T_fv().setField(Tsrv.globalVar.fieldName.graphData).setValue(''),
      new ts.T_fv().setField(Tsrv.globalVar.fieldName.verNum).setValue(''),
      new ts.T_fv().setField(Tsrv.globalVar.fieldName.m2oFunTitle).setValue('')
    ];
  }
}
interface I_cmd extends T_cmd {}
export { I_cmd, T_cmd };
